package indi.kider.server.bill.service;

import com.google.common.base.Strings;
import indi.kider.common.constant.BillClientStateCons;
import indi.kider.common.constant.BillTypeCons;
import indi.kider.common.constant.PayCons;
import indi.kider.common.constant.PayStateCons;
import indi.kider.common.model.apabus.Rent;
import indi.kider.common.model.apartment.Houses;
import indi.kider.common.model.bill.Bill;
import indi.kider.common.model.bill.BillPrepaidInfo;
import indi.kider.server.apabus.mapper.RentMapper;
import indi.kider.server.apartment.mapper.HousesMapper;
import indi.kider.server.bill.exception.ProduceBillException;
import indi.kider.server.bill.mapper.BillMapper;
import indi.kider.server.bill.mapper.BillPrepaidInfoMapper;
import indi.kider.server.bill.model.MeterAnbsValue;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;
import java.util.Date;
import java.util.List;

/**
 * Float 是否需要BigDecimal进行精度计算？
 */
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Service
@Slf4j
public class ProduceBillServiceImpl implements ProduceBillService {

    private static final String BILLING = "1";

    @Autowired
    private RentMapper            rentMapper;
    @Autowired
    private BillMapper            billMapper;
    @Autowired
    private HousesMapper          housesMapper;
    @Autowired
    private BillPrepaidInfoMapper billPrepaidInfoMapper;
    @Autowired
    private MeterCentreUtil       meterCentreUtil;

    @Override
    public void produceAllRentBill() {
        List<Rent> list = rentMapper.rentBillSoures(PayCons.POSTPAID);
        list.stream().forEach(rent -> {
            try {
                // 借用字段映射到h_houses.doPriceBill 字段
                // todo 重新写实体关系。
                String doPriceBill = rent.getWxopenId();
                if (BILLING.equals(doPriceBill)) {
                    produceRentBill(rent, false);
                }
            } catch (Exception e) {
                // todo 处理告警
                log.debug("生产所有需要的房租账单异常【{}】", e.getMessage());
            }
        });
    }

    /**
     * 次方法只有每个月的1号使用, 如果不是1号不执行
     */
    @Override
    public void produceAllConsumeBill() {
        // 如果不是1号不执行
        LocalDate now   = LocalDate.now();
        LocalDate first = now.with(TemporalAdjusters.firstDayOfMonth());
        if (now.equals(first)) {
            log.info("【今天是1号，开始计算上个月的账单】");
            DateTimeFormatter fmt     = DateTimeFormatter.ofPattern("yyyyMM");
            String            dateStr = now.minusMonths(1).format(fmt);
            produceAllConsumeBill(dateStr);
        } else {
            log.info("【今天不是1号，无法计算上个月的账单】");
        }
    }

    @Override
    public void produceAllConsumeBill(String billMonth) {
        List<Rent> list = rentMapper.rentConsumeBillSoures();
        list.stream().forEach(rent -> {
            try {
                settlementBill(rent, billMonth);
            } catch (ProduceBillException e) {
                log.debug("请求数据异常【{}】", e.getMessage());
            } catch (Exception e) {
                // todo 处理告警
                log.debug("生产所有消费的账单异常【{}】", e.getMessage());
            }
        });
    }

    @Override
    public Bill produceRentBill(Long rentId) {
        return produceRentBill(rentId, false);
    }

    @Override
    public Bill produceCreditRentBill(Long rentId) {
        return produceRentBill(rentId, true);
    }

    @Override
    public Bill produceCustomBill(Long rentId, Bill bill) {
        bill.setBillAttribution(rentId);
        /* 如果是房租 ，需要更新下次房租日期 。*/
        if (BillTypeCons.RENTAL.equals(bill.getBillType())) {
            Date date = plusMonths(bill.getBillTime(), bill.getAbsoluteNumber().longValue());
            updateBillDate(rentId, date);
        }
        insertOrUpdateSelective(bill);
        return bill;
    }

    @Override
    public int delBill(Long billId) {
        Bill bill = billMapper.selectByPrimaryKey(billId);
        // 未交状态直接可以删除
        if (PayStateCons.UNPAYED.equals(bill.getPayState())) {
            billMapper.deleteByPrimaryKey(billId);
            Date date = plusMonths(bill.getBillTime(), (0 - bill.getAbsoluteNumber().longValue()));
            updateBillDate(bill.getBillAttribution(), date);
            return 0;
        } else {
            return 1;
        }
    }

    @Override
    public Bill produceWaterBill(Rent rent, BigDecimal absn, String month) {
        Float nmb   = absn.floatValue();
        Float price = rent.getWaterCost();
        Float total = price * nmb;

        Bill waterBill = buildBill(month, BillTypeCons.WATER, rent.getId());

        waterBill.setAbsoluteNumber(nmb);
        waterBill.setPrice(price);
        waterBill.setTotal(total);

        insertOrUpdateSelective(waterBill);
        return waterBill;
    }

    @Override
    public Bill produceWaterPublicBill(Rent rent, BigDecimal absn, String month) {
        Float nmb   = absn.floatValue();
        Float price = rent.getWaterCost();
        Float total = price * nmb;

        Bill waterShareBill = buildBill(month, BillTypeCons.W_SHARE, rent.getId());

        waterShareBill.setAbsoluteNumber(nmb);
        waterShareBill.setPrice(price);
        waterShareBill.setTotal(total);

        insertOrUpdateSelective(waterShareBill);
        return waterShareBill;
    }

    @Override
    public Bill producePowerBill(Rent rent, BigDecimal absn, String month) {
        Float nmb   = absn.floatValue();
        Float price = rent.getPowerCost();
        Float total = price * nmb;

        Bill powerBill = buildBill(month, BillTypeCons.POWER, rent.getId());

        powerBill.setAbsoluteNumber(nmb);
        powerBill.setPrice(price);
        powerBill.setTotal(total);

        insertOrUpdateSelective(powerBill);
        return powerBill;
    }

    @Override
    public Bill producePowePublicBill(Rent rent, BigDecimal absn, String month) {
        Float nmb   = absn.floatValue();
        Float price = rent.getPowerCost();
        Float total = price * nmb;

        Bill powerPublicBill = buildBill(month, BillTypeCons.P_SHARE, rent.getId());

        powerPublicBill.setAbsoluteNumber(nmb);
        powerPublicBill.setPrice(price);
        powerPublicBill.setTotal(total);

        insertOrUpdateSelective(powerPublicBill);
        return powerPublicBill;
    }

    @Override
    public Bill produceNetBill(Rent rent, String month) {
        Float nmb   = 1f;
        Float price = rent.getNetCost();
        Float total = price * nmb;

        Bill netBill = buildBill(month, BillTypeCons.NET, rent.getId());
        netBill.setAbsoluteNumber(nmb);
        netBill.setPrice(price);
        netBill.setTotal(total);

        insertOrUpdateSelective(netBill);
        return netBill;
    }

    @Override
    public Bill producePropertyBill(Rent rent, String month) {
        Float nmb   = 1f;
        Float price = rent.getPropertyCosts();
        Float total = price * nmb;

        Bill propertyBill = buildBill(month, BillTypeCons.ESTATE, rent.getId());
        propertyBill.setAbsoluteNumber(nmb);
        propertyBill.setPrice(price);
        propertyBill.setTotal(total);

        insertOrUpdateSelective(propertyBill);
        return propertyBill;
    }

    @Override
    public int settlementBill(MeterAnbsValue meterAnbsValue) {
        Rent   rent   = rentMapper.selectByPrimaryKey(meterAnbsValue.getRentId());
        Houses houses = housesMapper.selectByPrimaryKey(rent.getHousesId());
        if ("2".equals(houses.getPostpaid())){
            return settlementBillPrepaid(meterAnbsValue, rent);
        } else {
            return settlementBillPostpaid(meterAnbsValue, rent);
        }

    }

    /**
     * 预付费的处理
     *
     * @param meterAnbsValue
     * @return
     */
    int settlementBillPrepaid(MeterAnbsValue meterAnbsValue, Rent rent) {
        Example example = new Example(BillPrepaidInfo.class);
        example.createCriteria().
                andEqualTo("payState", 0).
                andEqualTo("billAttribution", rent.getId());

        List<BillPrepaidInfo> list = billPrepaidInfoMapper.selectByExample(example);
        if (list == null || list.isEmpty())
            return 0;

        return 3;
    }

    /**
     * 后付费的处理
     *
     * @param meterAnbsValue
     * @return
     */
    int settlementBillPostpaid(MeterAnbsValue meterAnbsValue, Rent rent) {
        /*LocalDate date = LocalDate.now();
        String    now  = date.format(DateTimeFormatter.ofPattern("yyyyMM"));

        String sd = getDayByTemporalAdjuster(now, TemporalAdjusters.firstDayOfMonth());
        sd = getTheLastDay(sd, trucDateToString(rent.getRentDate()), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        // 结束时间取当前时间
        String ed = date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));

        try {
            // 请求大关接口
            MeterAnbsValue mav = meterCentreUtil.queryCrmFromServers(rent.getTenantId().toString(),
                    sd, ed, meterAnbsValue.getPr(), meterAnbsValue.getWr(), true);
            mav.setMonth(now);
            mav.setSdate(sd);
            mav.setEdate(ed);
            return settlementBill(mav, rent);
        } catch (ProduceBillException e) {
            return 2;
        }*/
        ///  固定账单
        String month =LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));

        // 水  水公摊
        BigDecimal wabsn = new BigDecimal("0");
        produceWaterBill(rent, wabsn, month);
        BigDecimal wpabsn = new BigDecimal("0");
        produceWaterPublicBill(rent, wpabsn, month);
        // 电 电公摊
        BigDecimal pabsn = new BigDecimal("0");
        producePowerBill(rent, pabsn, month);
        BigDecimal ppabsn = new BigDecimal("0");
        producePowePublicBill(rent, ppabsn, month);
        // 网费
        produceNetBill(rent, month);
        // 物业
        producePropertyBill(rent, month);
        return 0;
    }

    int insertOrUpdateSelective(Bill bill) {
        /*// 判断账单值为0 不插入
        if (Math.abs(bill.getTotal()) > 0.001f) {
            // 账单对租客可见
            bill.setState(1);
            return billMapper.insertOrUpdateSelective(bill);
        }
        log.info("【账单金额为0，不生成账单，参数{}】", bill);
        return 0;*/
        return billMapper.insertOrUpdateSelective(bill);
    }

    /**
     * 时间加月。
     *
     * @param date
     * @param m
     * @return
     */
    static Date plusMonths(Date date, long m) {
        LocalDate localDate = Instant.ofEpochMilli(date.
                getTime()).
                atZone(ZoneId.systemDefault()).toLocalDate();

        localDate = localDate.plusMonths(m);
        return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    static String getDayByTemporalAdjuster(String month, TemporalAdjuster temporalAdjusters) {
        DateTimeFormatter df1     = DateTimeFormatter.ofPattern("yyyyMMdd");
        DateTimeFormatter df2     = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate         ldMonth = LocalDate.parse(month + "17", df1);
        return df2.format(ldMonth.with(temporalAdjusters));
    }

    /**
     * 自己获取用量绝对值
     *
     * @param rent
     * @param month
     * @return
     */
    int settlementBill(Rent rent, String month) throws ProduceBillException {
        /*获取到服务器上的MeterAnbsValue*/
        Long   tenantId = rent.getTenantId();
        String sd       = getDayByTemporalAdjuster(month, TemporalAdjusters.firstDayOfMonth());
        sd = getTheLastDay(sd, trucDateToString(rent.getRentDate()), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        String ed = getDayByTemporalAdjuster(month, TemporalAdjusters.lastDayOfMonth());


        MeterAnbsValue mav = meterCentreUtil.queryCrmFromServers(tenantId.toString(),
                sd, ed, null, null, false);
        mav.setMonth(month);
        mav.setSdate(sd);
        mav.setEdate(ed);
        return settlementBill(mav, rent);
    }


    /**
     * 由前端去获取绝对值
     *
     * @param meterAnbsValue
     * @param rent
     * @return
     */
    @Transactional
    int settlementBill(MeterAnbsValue meterAnbsValue, Rent rent) {
        try {
            MeterAnbsValue mav   = meterCentreUtil.addProportion(meterAnbsValue, rent);
            String         month = meterAnbsValue.getMonth();
            if (Strings.isNullOrEmpty(month)) {
                LocalDate         date = LocalDate.now();
                DateTimeFormatter fmt  = DateTimeFormatter.ofPattern("yyyyMM");
                month = date.format(fmt);
                log.info("【参数mont为空，取当前的月{}为账单月】", month);
            }

            log.info("结算【{}】【{}】的账单", rent, month);
            // 水  水公摊
            BigDecimal wabsn = new BigDecimal(mav.getWaterConsumeValue());
            produceWaterBill(rent, wabsn, month);
            BigDecimal wpabsn = new BigDecimal(mav.getWaterPublicValue());
            produceWaterPublicBill(rent, wpabsn, month);
            // 电 电公摊
            BigDecimal pabsn = new BigDecimal(mav.getEleConsumeValue());
            producePowerBill(rent, pabsn, month);
            BigDecimal ppabsn = new BigDecimal(mav.getElePublicValue());
            producePowePublicBill(rent, ppabsn, month);
            // 网费
            produceNetBill(rent, month);
            // 物业
            producePropertyBill(rent, month);

//        } catch (ProduceBillException e) {
//            log.info("【该用户入住时间不需要结算账单】");
//            return 0;
        } catch (Exception e) {
            log.debug("结算账单异常【{}】", e.getMessage());
            return 1;
        }
        return 0;
    }


    /**
     * 防止rent的修改错误, 修改房租的下次时间。
     *
     * @param rentId
     * @param date
     * @return
     */
    int updateBillDate(Long rentId, Date date) {
        Rent updateRent = new Rent();
        updateRent.setId(rentId);
        updateRent.setBillDate(date);

        return rentMapper.updateByPrimaryKeySelective(updateRent);
    }

    Bill produceRentBill(Long rentId, boolean credit) {
        Rent rent = rentMapper.selectByPrimaryKey(rentId);
        return produceRentBill(rent, credit);
    }

    @Transactional
    Bill produceRentBill(Rent rent, boolean credit) {
        log.info("开始产生【{}】房租账单，是否直接结清【{}】", rent, credit);
        int     cycle = 1;
        Integer sl    = rent.getSetLease();
        if (sl > 1) {
            cycle = (sl - 1) * 3;
        }
        Float price = rent.getPriceSet();
        if (null == price) {
            price = 0f;
        }
        Float             total   = cycle * price;
        LocalDate         date    = LocalDate.now();
        DateTimeFormatter fmt     = DateTimeFormatter.ofPattern("yyyyMM");
        String            dateStr = date.format(fmt);
        Bill              bill    = buildBill(dateStr, BillTypeCons.RENTAL, rent.getId());

        bill.setAbsoluteNumber((float) cycle);
        bill.setPrice(price);
        bill.setTotal(total);

        if (credit) {
            bill.setPayState(PayStateCons.PAYED);
            bill.setRemark("系统结算业务，清算");
        }
        return produceCustomBill(rent.getId(), bill);
    }

    Bill buildBill(String month, Integer billType, Long billAttribution) {
        Bill bill = new Bill();
        bill.setBillType(billType);
        bill.setBillTime(new Date());
        bill.setState(BillClientStateCons.VALID);
        bill.setPayState(PayStateCons.UNPAYED);
        bill.setRefund(1);
        bill.setBillMonth(month);
        bill.setBillAttribution(billAttribution);
        return bill;
    }

    /**
     * 两个时间比较，去后面的时间
     * <p>
     * 日期格式为yyyy-MM-dd
     *
     * </p>
     *
     * @param d1
     * @param d2
     * @param dateTimeFormatter
     * @return
     */
    static String getTheLastDay(String d1, String d2, DateTimeFormatter dateTimeFormatter) {
        LocalDate date1 = LocalDate.parse(d1, dateTimeFormatter);
        LocalDate date2 = LocalDate.parse(d2, dateTimeFormatter);
        return date1.isBefore(date2) ? d2 : d1;
    }

    static String trucDateToString(Date date) {
        Instant       instant       = date.toInstant();
        ZoneId        zone          = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        LocalDate     localDate     = localDateTime.toLocalDate();
        return localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    }


//    public static void main(String[] args) {
//        Map<String,String> map = new HashMap<String,String>();
//        map.put("tenantId", "2873");
//        map.put("startDate", "2018-07-01");
//        map.put("endDate", "2018-07-30");

//        map.put("type", "1");
//        String str = null;
//        try {
//            str = HttpClientUtils.postForm("http://47.96.224.247:9001/meterdata/getMeterConsumptionByTenant",map,null, 10000, 10000);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        //JSON.toJSONString(str)
//        CommRespModel crm = JSON.parseObject(str, CommRespModel.class);
//        System.out.print(crm.getData());
//    }
}
